"""
Case Type   : cm_ctl可靠性
Case Name   : RTO模式下，cm_ctl switchover-主备switchover，kill -9 CM主进程
Create At   : 2022-07-18
Owner       : opentestcase012
Description :
    1.使用cm_ctl工具查看集群状态
    2.开启极致RTO，重启数据库
    3.主节点执行事务
    4.session1中同步在执行switchover节点下kill -9 CM主进程
    5.session2中在任一备节点执行switchover
    6.查看集群状态备节点切换是否成功
    7.查看CM主进程
Expect      :
    1.集群状态正常
    2.开启极致RTO成功，重启数据库成功
    3.主节点刷新事务成功
    4.kill CM主进程成功，实际进程会立即被拉起（pid发生变化）
    5.主节点事务连接失败，备节点执行switchover成功
    6.集群状态正常，主备节点发生切换，被切换备节点升为主节点，原主节点成为备节点
    7.CM主进程存在
History     :
"""

import os
import time
import unittest

from testcase.utils.ComThread import ComThread
from testcase.utils.Common import Common
from testcase.utils.CommonSH import CommonSH
from testcase.utils.Constant import Constant
from testcase.utils.Logger import Logger
from yat.test import Node
from yat.test import macro

Primary_SH = CommonSH('PrimaryDbUser')


@unittest.skipIf(3 != Primary_SH.get_node_num(), '需1主2备环境，否则不执行')
class DefinedResource(unittest.TestCase):
    def setUp(self):
        self.log = Logger()
        self.log.info(f'-----{os.path.basename(__file__)} start-----')
        self.pri_user = Node('PrimaryDbUser')
        self.pri_root = Node('PrimaryRoot')
        self.sta1_user = Node('Standby1DbUser')
        self.sta2_user = Node('Standby2DbUser')
        self.constant = Constant()
        self.common = Common()
        self.sh_primary = CommonSH('PrimaryDbUser')
        self.sh_stanby = CommonSH('Standby1DbUser')
        self.default_values = []
        self.param_lists = ['replication_type', 'recovery_parse_workers',
                            'recovery_redo_workers', 'hot_standby']
        self.sh_file = os.path.join(macro.CMA_DIR_PATH, "cm_test.sh")
        self.sh_logfile = os.path.join(macro.CMA_DIR_PATH, "cm_test.log")

    def test_cm(self):
        text = '----查看参数默认值; expect:成功----'
        self.log.info(text)
        for param in self.param_lists:
            value = self.sh_primary.execut_db_sql(f'show {param};')
            self.log.info(value)
            self.default_values.append(value.strip().splitlines()[-2])
        self.log.info(f'参数得默认值为：{self.default_values}')

        text = '----step1:使用cm_ctl工具查看集群状态; expect:集群状态正常----'
        self.log.info(text)
        show_status = f'source {macro.DB_ENV_PATH};' \
            f'cm_ctl query -i -v -C -d'
        self.log.info(show_status)
        status_msg = self.common.get_sh_result(self.pri_user, show_status)
        self.assertIn(self.constant.CLUSTER_NORMAL_MSG, status_msg,
                      "执行失败:" + text)

        self.assertIn(f'6001 {macro.DB_INSTANCE_PATH} P Primary Normal',
                      status_msg, "执行失败:" + text)
        self.assertIn(f'6002 {macro.DB_INSTANCE_PATH} S Standby Normal',
                      status_msg, "执行失败:" + text)
        self.assertIn(f'6003 {macro.DB_INSTANCE_PATH} S Standby Normal',
                      status_msg, "执行失败:" + text)

        text = '----step2:开启极致RTO,重启数据库; expect:成功----'
        self.log.info(text)
        value_list = [1, 4, 4, 'off']
        for i in range(4):
            if self.default_values[i] != value_list[i]:
                param, value = self.param_lists[i], value_list[i]
                result = self.sh_primary.execute_gsguc(
                    'set', self.constant.GSGUC_SUCCESS_MSG, f'{param}={value}')
                self.assertTrue(result, "执行失败:" + text)

        text = '----重启数据库; expect:成功----'
        self.log.info(text)
        restart_cmd = f'source {macro.DB_ENV_PATH};' \
            f'cm_ctl stop && cm_ctl start;'
        self.log.info(restart_cmd)
        restart_msg = self.common.get_sh_result(self.pri_user, restart_cmd)
        self.assertIn(self.constant.cm_start_success_msg, restart_msg,
                      "执行失败:" + text)
        self.assertIn(self.constant.cm_stop_success_msg, restart_msg,
                      "执行失败:" + text)

        text = '----查看CM主进程----'
        self.log.info(text)
        cms_pro_cmd = '''ps ux|grep "cm_server"|grep -v grep|\
        tr -s ' '|cut -d ' ' -f 2'''
        self.log.info(cms_pro_cmd)
        process_cms = self.common.get_sh_result(self.pri_user, cms_pro_cmd)
        self.assertNotEqual('', process_cms, "执行失败:" + text)

        text = '----拷贝修改cm_test.sh文件; expect:成功----'
        self.log.info(text)
        self.common.scp_file(self.pri_user, 'cm_test.sh', macro.CMA_DIR_PATH)
        scp_cmd = f"chmod +x {self.sh_file};" \
            f"sed -i 's#env_path#{macro.DB_ENV_PATH}#g' {self.sh_file};" \
            f"sed -i 's#port#{self.pri_user.db_port}#g' {self.sh_file};" \
            f"sed -i 's#database#{self.pri_user.db_name}#g' {self.sh_file};"
        self.log.info(scp_cmd)
        scp_msg = self.common.get_sh_result(self.pri_user, scp_cmd)
        self.assertNotIn('failed', scp_msg, '执行失败:' + text)
        cat_cmd = f"cat {self.sh_file};"
        scp_msg = self.common.get_sh_result(self.pri_user, cat_cmd)
        self.assertIn(macro.DB_ENV_PATH, scp_msg, '执行失败:' + text)
        self.assertIn(self.pri_user.db_port, scp_msg, '执行失败:' + text)

        text = '----step3:主节点执行事务; expect:主节点刷新事务成功----'
        self.log.info(text)
        sh_cmd = f'sh {self.sh_file} &>{self.sh_logfile} &'
        self.log.info(sh_cmd)
        sh_thread = ComThread(self.pri_user.sh, args=(sh_cmd,))
        sh_thread.setDaemon(True)
        sh_thread.start()
        sh_thread.join(120)

        self.log.info('----查看主节点事务执行结果----')
        cat_cmd = f'cat {self.sh_logfile}| tail -n 50;'
        self.log.info(cat_cmd)
        cat_msg = self.common.get_sh_result(self.pri_user, cat_cmd)
        for err_msg in self.constant.SQL_WRONG_MSG:
            self.assertNotIn(err_msg, cat_msg, '执行失败:' + text)

        try:
            text = '----step4:session1中同步在执行switchover节点下' \
                   'kill -9 CM主进程; expect:kill CM主进程成功，实际进程会立即被拉起----'
            self.log.info(text)
            kill_cmd = '''ps ux|grep "cm_server"|grep -v grep|\
            tr -s ' '|cut -d ' ' -f 2|xargs kill -9;'''
            kill_thread = ComThread(self.pri_user.sh, args=(kill_cmd,))
            kill_thread.setDaemon(True)
            kill_thread.start()
            kill_thread.join(120)
            kill_res = kill_thread.get_result()
            self.log.info(kill_res)
            self.assertEqual('', kill_res, "执行失败:" + text)

            text = '----step5:session2中在任一备节点执行switchover; ' \
                   'expect:主节点事务连接失败，备节点执行switchover成功----'
            self.log.info(text)
            time.sleep(5)
            switchover_cmd = f'source {macro.DB_ENV_PATH};' \
                f'cm_ctl switchover -n 3 -D {macro.DB_INSTANCE_PATH};'
            self.log.info(switchover_cmd)
            switchover_thread = ComThread(self.sta2_user.sh,
                                          args=(switchover_cmd,))
            switchover_thread.setDaemon(True)
            switchover_thread.start()

            text = '----获取switchover结果; expect:switchover成功----'
            self.log.info(text)
            switchover_thread.join(300)
            result = switchover_thread.get_result()
            self.log.info(result)
            self.assertIn(self.constant.cm_switchover_success_msg, result,
                          "执行失败:" + text)

            text = '----查看主节点事务执行结果; expect:主节点事务连接失败----'
            self.log.info(text)
            cat_cmd = f'cat {self.sh_logfile};'
            self.log.info(cat_cmd)
            cat_msg = self.common.get_sh_result(self.pri_user, cat_cmd)
            error_msg = 'FATAL:  can not accept connection if hot standby off'
            self.assertIn(error_msg, cat_msg, "执行失败:" + text)
            self.assertIn('failed to connect', cat_msg, "执行失败:" + text)
        finally:
            time.sleep(300)
            text = '----step6:查看集群状态; expect:集群状态正常，主备切换成功----'
            self.log.info(text)
            show_status = f'source {macro.DB_ENV_PATH};' \
                f'cm_ctl query -i -v -C -d'
            self.log.info(show_status)
            status_msg = self.common.get_sh_result(self.pri_user, show_status)
            self.assertIn(self.constant.CLUSTER_NORMAL_MSG, status_msg,
                          "执行失败:" + text)
            self.assertIn('P Standby Normal', status_msg, "执行失败:" + text)
            self.assertIn('S Primary Normal', status_msg, "执行失败:" + text)

            text = '----step7:查看CM主进程; expect:CM主进程存在----'
            self.log.info(text)
            self.log.info(cms_pro_cmd)
            process_new = self.common.get_sh_result(self.pri_user, cms_pro_cmd)
            self.assertNotEqual('', process_new, "执行失败:" + text)
        self.assertNotEqual(process_new, process_cms, "执行失败:" + text)

    def tearDown(self):
        text = '-----清理环境-----'
        self.log.info(text)
        time.sleep(5)
        text1 = '-----删除sh文件; expect:删除成功-----'
        self.log.info(text1)
        kill_cmd = f"ps ux|grep \"{self.sh_file}\"|grep -v grep|" \
            f"tr -s ' '|cut -d ' ' -f 2|xargs kill -9;"
        self.log.info(kill_cmd)
        self.common.get_sh_result(self.pri_user, kill_cmd)
        rm_cmd = f'rm -rf {self.sh_logfile} {self.sh_file};'
        self.log.info(rm_cmd)
        self.common.get_sh_result(self.pri_root, rm_cmd)
        check_cmd = f'if [[ -e {self.sh_logfile} && -e {self.sh_file} ]]; ' \
            f'then echo "exists"; else echo "not exists"; fi'
        rm_msg_pri = self.common.get_sh_result(self.pri_root, check_cmd)

        text2 = '----关闭RTO; expect:成功----'
        self.log.info(text2)
        mod_res = []
        for i in range(4):
            param, value = self.param_lists[i], self.default_values[i]
            result = self.sh_primary.execute_gsguc(
                'set', self.constant.GSGUC_SUCCESS_MSG, f'{param}={value}')
            mod_res.append(result)
        self.log.info(mod_res)

        text3 = '----重启集群; expect:成功----'
        self.log.info(text3)
        restart_cmd = f'source {macro.DB_ENV_PATH};' \
            f'cm_ctl stop && cm_ctl start;'
        self.log.info(restart_cmd)
        restart_msg = self.common.get_sh_result(self.pri_user, restart_cmd)

        text4 = '----主备恢复为初始状态; expect:成功----'
        self.log.info(text4)
        switch_cmd = f'source {macro.DB_ENV_PATH};' \
            f'cm_ctl switchover -a;' \
            f'cm_ctl set --cmsPromoteMode=PRIMARY_F -I 1;' \
            f'cm_ctl set --cmsPromoteMode=AUTO -I 1;' \
            f'sleep 5;' \
            f'cm_ctl query -i -v -C -d;'

        self.log.info(switch_cmd)
        switch_msg = self.common.get_sh_result(self.pri_user, switch_cmd)

        text5 = '----数据恢复为初始状态; expect:成功----'
        del_cmd = "drop table t_cm_001 cascade;"
        del_res = self.sh_primary.execut_db_sql(del_cmd)
        self.log.info(del_res)
        self.assertIn(del_res, ('ERROR:  table "t_cm_001" does not exist', 'DROP TABLE'), "执行失败:" + text5)

        self.assertEqual('not exists', rm_msg_pri, '执行失败:' + text1)
        self.assertEqual(4, mod_res.count(True), '执行失败:' + text2)
        self.assertIn(self.constant.cm_start_success_msg, restart_msg,
                      "执行失败:" + text3)
        self.assertIn(self.constant.cm_stop_success_msg, restart_msg,
                      "执行失败:" + text3)
        self.assertIn(self.constant.cm_switchover_success_msg, switch_msg,
                      "执行失败:" + text4)
        self.assertIn('P Primary Normal', switch_msg, "执行失败:" + text4)
        self.log.info(f'-----{os.path.basename(__file__)} end-----')

